%load_ext pretty_jupyter

Importar Bibliotecas

import pandas as pd
import os
import numpy as np
# Worcloud
from PIL import Image
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
# Gráficos
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import seaborn as sns

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px

# Salvar o gráfico como um arquivo HTML
import plotly.io as pio
# Calcular a correlação
from scipy.stats import pearsonr
from scipy.stats import spearmanr
import numpy as np
import statsmodels.api as sm
# tabset
import jinja2
get_ipython().run_line_magic('load_ext', 'pretty_jupyter')

Definir Diretório

os.chdir("C:/0.Projetos/5.Sistema_de_Recomendacao_MovieLens_2")

Carregar Arquivos

genome_treino= pd.read_pickle("C:/0.Projetos/5.Sistema_de_Recomendacao_MovieLens_2/Datasets/3.Datasets_Transformação/3.2_Datasets_Transformação_parte_2/genome_treino.pickle", compression='gzip')
genome_treino.head(3)
tags_treino = pd.read_pickle("C:/0.Projetos/5.Sistema_de_Recomendacao_MovieLens_2/Datasets/2.Datasets_Limpeza/tags_treino.pickle", compression = "gzip")
tags_treino
ratings_treino_transformado = pd.read_pickle("C:/0.Projetos/5.Sistema_de_Recomendacao_MovieLens_2/Datasets/3.Datasets_Transformação/3.1_Datasets_Transformação_parte_1/ratings_treino_transformado.pickle", compression = "gzip")
ratings_treino_transformado.head(3)
movies_treino_transformado = pd.read_pickle("C:/0.Projetos/5.Sistema_de_Recomendacao_MovieLens_2/Datasets/3.Datasets_Transformação/3.2_Datasets_Transformação_parte_2/movies_treino_transformado.pickle", compression = 'gzip')
movies_treino_transformado.head(3) 

Funções

Função 1: Calcula o Resumo Estatístico

def resumo_estatistico(tabela:pd.DataFrame, coluna:str) -> pd.DataFrame:

    # Calcula estatísticas descritivas usando agg
    resumo = tabela[coluna].agg(['count', 'mean','median', 'std', 'min', 'max' ]).round(2)

    # Renomeiar a coluna 'median' para 'median (50%)'
    resumo = resumo.rename(index={'median': 'median (50%)'})

    # Calcula percentis usando quantile
    percentis = tabela[coluna].quantile([0.25, 0.5, 0.75]).round(2)

    # Combina os resultados em um DataFrame único
    resumo = pd.concat([resumo, percentis.rename({0.25: '25%', 0.5: '50%', 0.75: '75%'})])

    display(resumo)

Função 2: Retorna a frequência e proporção dos valores únicos

def valores_unicos(tabela:pd.DataFrame, coluna:str) -> pd.DataFrame:
    
    # Calcula a contagem dos valores únicos usando value_counts
    contagem = tabela[coluna].value_counts().reset_index()

    # Renomeia as colunas para tornar o resultado mais claro
    contagem.columns = ['Valor_Único', 'Frequência']

    # Adiciona uma coluna de proporção em porcentagem
    contagem['Proporção (%)'] = (contagem['Frequência'] / contagem['Frequência'].sum()) * 100

    # Arredonda a coluna de proporção para 4 casas decimais
    contagem['Proporção (%)'] = contagem['Proporção (%)'].round(4)

    # Formata a coluna de proporção como porcentagem
    contagem['Proporção (%)'] = contagem['Proporção (%)'].map('{:.4f}%'.format)


    return contagem

1. Quantidade de usuários, de filmes e de gêneros

Nesta amostra, existem 7.943 clientes, 24.345 filmes e 19 gêneros de filmes.

quantidade_user = ratings_treino_transformado['userId'].nunique()
quantidade_filmes = ratings_treino_transformado['movieId'].nunique()
quantidade_generos = movies_treino_transformado['genres_separado'].nunique()
print(f"Quantidade de usuários: {quantidade_user} ")
print(f"Quantidade de filmes: {quantidade_filmes} ")
print(f"Quantidade de genero: {quantidade_generos} ")

Gráfico: usuários, filmes e gêneros

# Criação do gráfico
plt.figure(figsize=(8, 4))

# Definição das posições dos pontos
x_positions = [0.5, 1.5, 2.5]
y_positions = [1, 1, 1]

# Definição das cores dos pontos
#colors = ['#06837f', 'orange'ou '#ffc675', '#6a0572']
#colors = ['#96c9c2','#ffc675' , '#b978b0']
colors = ['#91C6D3','#FFA051' , '#CB85FF']
# Definição dos tamanhos dos pontos
sizes = [15000, 15000, 15000]

# Criação dos pontos no gráfico
plt.scatter(x=x_positions, y=y_positions, s=sizes, color=colors)

# Definição dos limites dos eixos
plt.xlim(0, 3)
plt.ylim(0.9, 1.2)

# Adição de títulos e rótulos
plt.title('Quantidade de Usuários, Filmes e Gêneros', fontsize=18, weight=600, color='#333d29')
plt.text(0.5, 1, f'{quantidade_user}\nUsuários', va='center', ha='center', fontsize=18, weight=600, color='black')
plt.text(1.5, 1, f'{quantidade_filmes}\nFilmes', va='center', ha='center', fontsize=18, weight=600, color='black')
plt.text(2.5, 1, f'{quantidade_generos}\nGêneros', va='center', ha='center', fontsize=18, weight=600, color='black')
plt.text(0.5, 1.11, 'Usuários', va='center', ha='center', fontsize=17, weight=500, color='#1c2541')
plt.text(1.5, 1.11, 'Filmes', va='center', ha='center', fontsize=17, weight=500, color='#1c2541')
plt.text(2.5, 1.11, 'Gêneros', va='center', ha='center', fontsize=17, weight=500, color='#1c2541')

# Desliga a exibição dos eixos
plt.axis('off')

# Exibição do gráfico
plt.show()

2. Distribuição estatística de Ratings

A média de ratings é 3,52 e as notas mais dadas são 4, 3, 5 e 3.5 respectivamente. Estes valores mostram que os filmes tem uma avaliação intermediária.

Resumo Estatístico de Ratings

resumo_estatistico(ratings_treino_transformado, 'rating')

Proporção dos Valores únicos de Ratings

valores_unicos(ratings_treino_transformado, 'rating')

Gráfico BoxPlot - Distribuição Rating

import seaborn as sns
import matplotlib.pyplot as plt

# Ajustar o estilo e a paleta de cores
sns.set(style="white", palette="muted")

# Ajustar a escala para melhor visualização
plt.figure(figsize=(9, 4))

# Criar o boxplot
ax = sns.boxplot(x=ratings_treino_transformado['rating'], 
                 color="#04819E",
                 medianprops={"color": "#FF7F00", "linewidth": 2},
                 showmeans=True,
                 meanprops={"markerfacecolor":"white", 
                            "markeredgecolor":"black",
                            "markersize":"10"})

# Adicionar legendas manuais
plt.plot([], [], color='#FF7F00', label='Mediana', linewidth=2)
plt.plot([], [], 'w^', label='Média', markersize=10, markeredgecolor="black")

# Adicionar título e rótulos aos eixos
plt.title('Distribuição de Rating', fontsize=18, fontweight='bold')
plt.xlabel('Rating', fontsize=14)
plt.ylabel('Contagem', fontsize=14)

# Exibir a legenda à esquerda
plt.legend(loc='upper right', fontsize=10, frameon=True, fancybox=True, shadow=True, borderpad=1)

# Ajustar a cor das bordas esquerda e direita
ax.spines['bottom'].set_color('#808080')  # definir a cor desejada para a borda esquerda
ax.spines['left'].set_color('#808080')  # definir a cor desejada para a borda direita
ax.spines['bottom'].set_linewidth(0.7)  # ajustar a largura da borda esquerda
ax.spines['left'].set_linewidth(0.7)  # ajustar a largura da borda direita

# Remover as bordas de cima e da direita
sns.despine(top=True, right=True)

# Ajustar os ticks dos eixos
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)

# Exibir o gráfico
plt.show()

Gráfico- Contagem de valores de Rating

import matplotlib.pyplot as plt
import seaborn as sns

# Criar um gráfico de contagem para a coluna ratings
plt.figure(figsize=(5, 4))
sns.set(style="whitegrid")
ax = sns.countplot(x=ratings_treino_transformado['rating'], color="#046D86")

# Adicionar título e rótulos aos eixos
plt.title('Contagem de ocorrências de cada valor de rating', fontsize=16, fontweight='bold')
plt.xlabel('Rating', fontsize=14)
plt.ylabel('Contagem', fontsize=14)

# Ajustar a cor das bordas
ax.spines['bottom'].set_color('#404040')
ax.spines['left'].set_color('#404040')

# Ajustar a largura das bordas 
ax.spines['bottom'].set_linewidth(0.9)
ax.spines['left'].set_linewidth(0.9)

# Adicionar bordas às barras
for patch in ax.patches:
    patch.set_edgecolor('#004050')  # cor da borda
    patch.set_linewidth(0.7)        # espessura da borda

# Remover as bordas de cima e da direita
sns.despine(top=True, right=True)

# Exibir o gráfico
plt.show()

3.Quantidade de filmes que um usuário avalia

Um usuário avalia em média 609 filmes e a mediana é 318. A maioria dos usuários faz 15 avaliações, seguidos de usuários que fazem 5.071 , 4.228, 4.135 e 16 avaliações, respectivamente.

Pensamos em duas hipóteses sobre os usuários que fazem 15 avaliações:
$H_0:$ Os usuários são novos no site, por isso, fizeram poucas avaliações.
$H_1:$ Os usuários utilizaram o site por um tempo e não ficaram satisfeitos. Por isso, deixaram de usá-lo.

Para entender qual é a hipótese mais provável, fizemos uma análise gráfica do número de avaliações por usuário e da média de ratings. Ao olhar o histograma, observa-se que a maioria das avaliações por usuários fica entre 0 e 1.000.
Por isso, criamos um outro gráfico cujo intervalo fica entre 0 e 1.000. E notamos que a partir de 40 avaliações por usuário, a contagem começa a diminuir.

Em relação a ratings, separamos o dataset em faixas e examinamos a média desta coluna. O que observamos é que , em média, ratings diminui conforme o número de avaliações aumenta.

$\uparrow$ Contagem do Número de Avaliações por usuário $\ \ \ \ $ $\downarrow$ Nota dada (rating)

Isto pode ser resultado de uma insatisfação dos usuários com as recomendações $(H_1)$.

ratings_treino_transformado.head()

Resumo Estatístico do Nº de Avaliações por Usuários

resumo_estatistico(ratings_treino_transformado,'Numero_de_Avaliacoes_por_usuarios' )

Rating por faixas

Separamos a coluna rating em diferentes faixas para identificar se, em média, há alguma relação com o aumento do número de avaliações por usuário.

filtro_0_40 = ratings_treino_transformado.loc[(ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 40)]
filtro_41_500 = ratings_treino_transformado.loc[
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= 40) &
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 500)
]

filtro_501_1000 = ratings_treino_transformado.loc[
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= 501) &
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 1000)
]

filtro_1001_1500 = ratings_treino_transformado.loc[
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= 1001) &
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 1500)
]

filtro_1501_2000 = ratings_treino_transformado.loc[
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= 1501) &
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 2000)
]

filtro_2001_3000 = ratings_treino_transformado.loc[
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= 2001) &
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 3000)
]

filtro_3001_4500 = ratings_treino_transformado.loc[
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= 3001) &
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 4500)
]

filtro_4501_5071 = ratings_treino_transformado.loc[
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= 4501) &
    (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 5071)
]
faixas = pd.DataFrame({
    'Faixas_Numero_de_avaliacoes':['0-40','41-500', '501-1000', '1001-1500', '1501-2000', '2001-3000', '3001-4500', '4501-5071'],
    'Faixas_Numero_de_avaliacoes2':[40,500, 1000, 1500, 2000,3000,4500, 5071],
    'Rating_por_faixa':[filtro_0_40['rating'].mean(), filtro_41_500['rating'].mean(),filtro_501_1000['rating'].mean(), filtro_1001_1500['rating'].mean(), filtro_1501_2000['rating'].mean(), filtro_2001_3000['rating'].mean(),filtro_3001_4500['rating'].mean(),filtro_4501_5071['rating'].mean()]
})
faixas

Gráfico Número de avaliações por usuário e rating

# Dados para o histograma
histogram_data = ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios']

# Dados para o grafico de linha
df_linha = faixas

# Criar o gráfico combo com histograma e linha
fig = go.Figure()

# Adicionar histograma
fig.add_trace(go.Histogram(
    x=histogram_data,
    nbinsx=100,
    marker_color="#7D01D6",
    marker_line_color='white',
    marker_line_width=1.5,
    name='Distribuição do número de avaliações de filme por usuário',
    yaxis='y1'  # Associando ao eixo y1 (à esquerda)
))

# Adicionar linha com médias de rating por faixa
fig.add_trace(go.Scatter(
    x=df_linha['Faixas_Numero_de_avaliacoes2'],
    y=df_linha['Rating_por_faixa'],
    mode='lines+markers',
    line=dict(color='#FF5733', width=2.9),
    marker=dict(color='#FF5733', size=9),
    name='Média de Ratings por Faixa de Número de Avaliações por Usuário',
    xaxis='x',
    yaxis='y2'  # Associando ao eixo y2 (à direita)
))

# Atualizar layout do gráfico
fig.update_layout(
    width=950,
    height=550,
    title='Distribuição do número de avaliações de filme por usuário e Média de Ratings por Faixa',
    title_font=dict(size=20, family='Arial', color='black', weight='bold'),  # Tamanho e estilo do título
    xaxis_title='Número de avaliações de filme por usuário',
    yaxis_title='Contagem',
     xaxis=dict(
        tickmode='linear',
        tick0=0,
        dtick=500,
        tickfont=dict(size=12),
    ),
    yaxis=dict(
        title='Contagem',
        side='left',  # Posicionar à esquerda
        showgrid=False,
        showticklabels=True,
        tickfont=dict(size=12),
    ),
    yaxis2=dict(
        title='Média de Rating',
        overlaying='y',
        side='right',  # Posicionar à direita
        showgrid=False,
        showticklabels=True,
        tickfont=dict(size=12),
    ),
    font=dict(family="Arial", size=12),
    legend=dict(
        x=0.5,  # Posição horizontal da legenda em relação ao gráfico
        y=1.05,   # Posição vertical da legenda em relação ao gráfico
        traceorder='normal',
        font=dict(
            family='Arial',
            size=12,
            color='black'
        ),
        bgcolor='rgba(255, 255, 255, 0.5)',
        bordercolor='rgba(0, 0, 0, 0.4)',
        borderwidth=1
    ),
    showlegend=True,
    plot_bgcolor='white'
)

# Exibir o gráfico
fig.show()
# Salvar o gráfico como um arquivo HTML
import plotly.io as pio
pio.write_html(fig, file='Distribuicao_Numero_de_avaliacoes_por_usuario.html', auto_open=True)

Comparar as médias de quem avalia muito e pouco

Vamos analisar a média da coluna rating dos usuários que avaliaram até 1.000 filmes e dos usuários que avaliaram mais de 1.000 filmes.

Selecionar apenas até 1.000 avaliações por usuário

filtro_1000 = ratings_treino_transformado.loc[(ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= 1000)]
filtro_1000.head(3)
import pandas as pd

# Definindo os intervalos e rótulos das faixas
faixas_definidas = [
    (0, 40),
    (11, 80),
    (81, 100),
    (101, 150),
    (151, 200),
    (201, 250),
    (251, 300),
    (301, 350),
    (351, 400),
    (401, 550),
    (551, 650),
    (651, 700),
    (701, 800),
    (801, 1000)
]

# Criando uma lista para armazenar os resultados
faixas_data = []

# Iterando sobre os intervalos
for faixa in faixas_definidas:
    faixa_min, faixa_max = faixa
    filtro = ratings_treino_transformado.loc[
        (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] >= faixa_min) &
        (ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'] <= faixa_max)
    ]
    faixa_label = f'{faixa_min}-{faixa_max}'
    rating_medio = filtro['rating'].mean()
    # Obtendo o último número da faixa
    ultimo_numero = faixa_max
    faixas_data.append({
        'Faixas_Numero_de_avaliacoes': faixa_label,
        'Faixas_Numero_de_avaliacoes2': ultimo_numero,
        'Rating_por_faixa': rating_medio
    })

# Criando o DataFrame a partir da lista de dicionários
faixas1 = pd.DataFrame(faixas_data)

# Exibindo o DataFrame com as faixas, o último número da faixa e as médias de rating
print(faixas1)
# Dados para o histograma
histogram_data = filtro_1000['Numero_de_Avaliacoes_por_usuarios']

# Dados para o grafico de linha
df_linha1 = faixas1

# Criar o gráfico combo com histograma e linha
fig = go.Figure()

# Adicionar histograma
fig.add_trace(go.Histogram(
    x=histogram_data,
    nbinsx=50,
    marker_color="#7D01D6",
    marker_line_color='white',
    marker_line_width=1.5,
    name='Distribuição do número de avaliações de filme por usuário',
    yaxis='y1'  # Associando ao eixo y1 (à esquerda)
))

# Adicionar linha com médias de rating por faixa
fig.add_trace(go.Scatter(
    x=df_linha1['Faixas_Numero_de_avaliacoes2'],
    y=df_linha1['Rating_por_faixa'],
    mode='lines+markers',
    line=dict(color='#FF5733', width=2.9),
    marker=dict(color='#FF5733', size=9),
    name='Média de Ratings por Faixa de Número de Avaliações por Usuário',
    xaxis='x',
    yaxis='y2'  # Associando ao eixo y2 (à direita)
))

# Atualizar layout do gráfico
fig.update_layout(
    width=1050,
    height=550,
    title='Distribuição do número de avaliações de filme por usuário e Média de Ratings por Faixa (até 1.000 avaliações)',
    title_font=dict(size=19, family='Arial', color='black', weight='bold'),  # Tamanho e estilo do título
    xaxis_title='Número de avaliações de filme por usuário',
    yaxis_title='Contagem',
     xaxis=dict(
        tickmode='linear',
        tick0=0,
        dtick=50,
        tickfont=dict(size=12),
    ),
    yaxis=dict(
        title='Contagem',
        side='left',  # Posicionar à esquerda
        showgrid=False,
        showticklabels=True,
        tickfont=dict(size=12),
    ),
    yaxis2=dict(
        title='Média de Rating',
        overlaying='y',
        side='right',  # Posicionar à direita
        showgrid=False,
        showticklabels=True,
        tickfont=dict(size=12),
    ),
    font=dict(family="Arial", size=12),
    legend=dict(
        x=0.5,  # Posição horizontal da legenda em relação ao gráfico
        y=1.05,   # Posição vertical da legenda em relação ao gráfico
        traceorder='normal',
        font=dict(
            family='Arial',
            size=12,
            color='black'
        ),
        bgcolor='rgba(255, 255, 255, 0.5)',
        bordercolor='rgba(0, 0, 0, 0.4)',
        borderwidth=1
    ),
    showlegend=True,
    plot_bgcolor='white'
)

# Exibir o gráfico
fig.show()
# Salvar o gráfico como um arquivo HTML
import plotly.io as pio
pio.write_html(fig, file='Distribuicao_Numero_de_avaliacoes_por_usuario_até_1000.html', auto_open=True)

4. Existe relação entre o número de avaliações por usuário e a nota dada ?

O objetivo desta análise é complementar o tópico 3. O resultado mostra que existe uma relação negativa , fraca, porém significativa entre a nota dada pelo usuário (rating) e o número de avaliações por usuário. Ou seja, conforme o número de avaliações aumenta, rating dimimui. O resultado destas correlações também é um indicativo de que com o passar do tempo, o usuário fica insatisfeito com as recomendações, dando uma nota menor nas avaliações dos filmes.
Conforme o resultado anterior, este também sinaliza que a Movilens precisa melhorar as recomendações dos filmes e encontrar maneiras de reter o usuário por mais tempo. Este tipo de análise também poderia ser feita por sites como NetFlix.

# Olhar as primeiras linhas da tabela que será usada
ratings_treino_transformado.head()

Correlação

Correlação de Spearman

correlacao_avaliacao1= spearmanr(ratings_treino_transformado['rating'], ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'])
print("Coeficiente de correlação de Spearman:", correlacao_avaliacao1.statistic)
print("Valor-p:", correlacao_avaliacao1.pvalue)

Correlação de Pearson

correlacao_avaliacao2 = pearsonr(ratings_treino_transformado['rating'], ratings_treino_transformado['Numero_de_Avaliacoes_por_usuarios'])
print("Coeficiente de correlação de Pearson:", correlacao_avaliacao2.statistic)
print("Valor-p:", correlacao_avaliacao2.pvalue)

Gráfico BoxPlot

Para fazer o gráfico, vamos utilizar o rating médio ponderado e agrupá-lo em intervalos de 0,5.

# Definindo os intervalos de 0.5 em 0.5 de 0 a 5
bins = np.arange(0, 5.5, 0.5)

# Função para arredondar para o intervalo mais próximo de 0,5
def arredondar(number):
    return round(number * 2) / 2

df = ratings_treino_transformado.copy()
# Aplicando a função ao DataFrame
df['media_ponderada_arredondada'] = df['rating_medio_ponderado'].apply(arredondar)
# Olhar a tabela para ver se deu certo
df.head()
# Definir o estilo e a paleta de cores
sns.set(style="white", palette="pastel")

# Criar o gráfico de dispersão
plt.figure(figsize=(16, 8))
sns.boxplot(x=df['media_ponderada_arredondada'], y=df['Numero_de_Avaliacoes_por_usuarios'], color='#06ADD4')

# Configurações de rótulos e título
plt.ylabel('Número de Avaliações por Usuário', fontsize=16, weight='bold')
plt.xlabel('Ratings Médio Ponderado (Agrupado)', fontsize=16, weight='bold')
plt.title('Distribuição do Número de Avaliações por Usuário em Diferentes Ratings', fontsize=18, weight='bold', pad=20)

# Remover as linhas de grade superior e direita e outras bordas desnecessárias
sns.despine(left=True, bottom=True)

# Ajuste das ticks
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)

# Ajustar layout para evitar sobreposição
plt.tight_layout()

# Mostrar o gráfico
plt.show()

5. Quais filmes possuem mais avaliações?

Observa-se que a maioria dos filmes com mais avaliações são do final da década de 90 e início dos anos 2000. O gênero, em sua maioria, é Ação, Comédia e Crime.
Os filmes mais avaliados são, respectivamente: The Shawshank Redemption (1994) ; Forrest Gump (1994) e Pulp Fiction (1994).
Mas ao olhar a nuvem de palavras, vemos outros filmes se destacando: Star Wars, Harry Potter, Batman e Lord of Ring. O que eles tem em comum é que todos tem continuações. Então, se analisarmos as sagas como um todo, esses filmes também são muito avaliados.

Olhar as tabelas que serão utilizadas

Vamos utilizar algumas colunas das tabelas movies_treino_transformado e ratings_treino_transformado.

# Olhar as primeiras linhas da tabela que será ultilizada
movies_treino_transformado.head()
# Olhar as primeiras linhas da tabela que será ultilizada
ratings_treino_transformado.head()

Selecionar as colunas de interesse

A seguir, selecionaremos as colunas de interesse, para em seguida fazer a união das tabelas.

# Criar uma subtabela de 'movies_genero_separado
tmp_movie = movies_treino_transformado[['movieId', 'title' , 'titulo_sem_ano', 'genres_separado']]
tmp_movie.head(3)
movieId title titulo_sem_ano genres_separado
0 1 Toy Story (1995) Toy Story Adventure
1 2 Jumanji (1995) Jumanji Adventure
2 3 Grumpier Old Men (1995) Grumpier Old Men Comedy
# Criar uma subtabela de 'ratings_data_hora3'
tmp_ratings = ratings_treino_transformado[['movieId','Numero_de_Avaliacoes_por_Filme']]
tmp_ratings.head(3)
movieId Numero_de_Avaliacoes_por_Filme
0 47 1567
1 47 1567
2 47 1567

Unir as tabelas

Após selecionar as colunas de interesse, vamos unir estas tabelas. Em seguida, removeremos as linhas duplicadas e organizaremos as tabelas de forma decrescente.

# Unir as subtabelas
filmes_mais_avaliados1 = pd.merge(tmp_movie , tmp_ratings, on='movieId' , how='right' )
filmes_mais_avaliados1.head(3) 
movieId title titulo_sem_ano genres_separado Numero_de_Avaliacoes_por_Filme
0 47 Seven (a.k.a. Se7en) (1995) Seven (a.k.a. Se7en) Mystery 1567
1 47 Seven (a.k.a. Se7en) (1995) Seven (a.k.a. Se7en) Mystery 1567
2 47 Seven (a.k.a. Se7en) (1995) Seven (a.k.a. Se7en) Mystery 1567
# Remover as linhas duplicadas
filmes_mais_avaliados2 = filmes_mais_avaliados1.drop_duplicates(subset='title')

# Organizar de forma decrescente
filmes_mais_avaliados3 = filmes_mais_avaliados2.sort_values(by='Numero_de_Avaliacoes_por_Filme', ascending=False)
filmes_mais_avaliados3
movieId title titulo_sem_ano genres_separado Numero_de_Avaliacoes_por_Filme
1811 318 Shawshank Redemption, The (1994) Shawshank Redemption, The Crime 2948
5454 356 Forrest Gump (1994) Forrest Gump Comedy 2700
41951 296 Pulp Fiction (1994) Pulp Fiction Comedy 2609
25480 2571 Matrix, The (1999) Matrix, The Action 2578
11878 593 Silence of the Lambs, The (1991) Silence of the Lambs, The Crime 2512
... ... ... ... ... ...
811300 27480 Dead or Alive 2: Tôbôsha (2000) Dead or Alive 2: Tôbôsha Action 1
811301 27549 Dead or Alive: Final (2002) Dead or Alive: Final Comedy 1
787658 6588 And Now... Ladies and Gentlemen... (2002) And Now... Ladies and Gentlemen... Romance 1
811316 31932 Fallen Angel (1945) Fallen Angel Crime 1
820507 165471 The Ring (1984) The Ring Drama 1

24326 rows × 5 columns

Tabela com os 10 filmes que possuem mais avaliações

Agora, nós temos a tabela final com os filmes que possuem mais avaliações.

# Selecionar as 30 primeiras linhas
filmes_mais_avaliados4 = filmes_mais_avaliados3.head(10) 
filmes_mais_avaliados4
movieId title titulo_sem_ano genres_separado Numero_de_Avaliacoes_por_Filme
1811 318 Shawshank Redemption, The (1994) Shawshank Redemption, The Crime 2948
5454 356 Forrest Gump (1994) Forrest Gump Comedy 2700
41951 296 Pulp Fiction (1994) Pulp Fiction Comedy 2609
25480 2571 Matrix, The (1999) Matrix, The Action 2578
11878 593 Silence of the Lambs, The (1991) Silence of the Lambs, The Crime 2512
39609 260 Star Wars: Episode IV - A New Hope (1977) Star Wars: Episode IV - A New Hope Action 2342
9808 527 Schindler's List (1993) Schindler's List Drama 2070
31725 2959 Fight Club (1999) Fight Club Action 2049
76326 480 Jurassic Park (1993) Jurassic Park Action 1984
48920 1196 Star Wars: Episode V - The Empire Strikes Back... Star Wars: Episode V - The Empire Strikes Back Action 1942

Gráfico com os 10 filmes que possuem mais avaliações

Gráfico 1

# Criar o gráfico de barras horizontais com Plotly
fig = px.bar(filmes_mais_avaliados4, 
             y='title', 
             x='Numero_de_Avaliacoes_por_Filme', 
             orientation='h',
             color_discrete_sequence=['#046D86'],
             title='Os 10 filmes que possuem mais avaliações',
             labels={'Numero_de_Avaliacoes_por_Filme': 'Número de Avaliações por Filme', 
                     'title': 'Título do Filme'})

# Ajustar layout para um visual mais minimalista
fig.update_layout(
    width=700,
    height=350,
    title=dict(
        text='Os 100 filmes com mais avaliações',
        x=0.5,  # Centralizar o título
        xanchor='center',
        font=dict(size=20, family='Arial', color='black', weight='bold')  # Negrito
    ),
    xaxis_title_font=dict(size=12, family='Arial', color='black'),
    yaxis_title_font=dict(size=14, family='Arial', color='black'),  # Aumentar tamanho da fonte
    font=dict(size=12),  # Aumentar tamanho da fonte geral
    plot_bgcolor='white',
    yaxis=dict(
        autorange='reversed',  # Inverter a ordem das barras
        tickfont=dict(size=14)  # Aumentar o tamanho dos valores do eixo y
    ),
    margin=dict(t=70, b=20, l=50, r=20)  # Ajustar as margens para mover o gráfico para cima
)

# Mostrar o gráfico
fig.show(renderer="notebook_connected")
# Salvar o gráfico como um arquivo HTML
import plotly.io as pio
pio.write_html(fig, file='0s_10_filmes_com_mais_avaliacoes.html', auto_open=True)
G1: Responsivo

Gráfico 2

import json
import pandas as pd
from IPython.display import display, HTML

# Supondo que 'filmes_mais_avaliados4' seja um DataFrame com os dados adequados

# Converter os dados para JSON
data = [
    {"name": row['title'], "y": row['Numero_de_Avaliacoes_por_Filme']}
    for index, row in filmes_mais_avaliados4.iterrows()
]

# Definir largura e altura desejadas
chart_width = 600
chart_height = 350

# Template HTML com Highcharts
html_template = """
<!DOCTYPE html>
<html>
<head>
    <title>Os 100 filmes com mais avaliações</title>
    <script src="https://code.highcharts.com/highcharts.js"></script>
    <script src="https://code.highcharts.com/highcharts-more.js"></script>
    <script src="https://code.highcharts.com/modules/exporting.js"></script>
    <script src="https://code.highcharts.com/modules/export-data.js"></script>
    <script src="https://code.highcharts.com/modules/accessibility.js"></script>
</head>
<body>
    <div id="container" style="width:{width}px; height:{height}px;"></div>
    <script>
        var data = {data};

        Highcharts.chart('container', {{
            chart: {{
                type: 'bar',
                width: {width},
                height: {height}
            }},
            title: {{
                text: 'Os 10 filmes com mais avaliações',
                align: 'center'
            }},
            xAxis: {{
                categories: {categories},
                title: {{
                    text: 'Título do Filme'
                }},
                labels: {{
                    style: {{
                        fontSize: '14px'
                    }}
                }}
            }},
            yAxis: {{
                min: 0,
                title: {{
                    text: 'Número de Avaliações por Filme',
                    align: 'high'
                }},
                labels: {{
                    enabled: false  // Desabilitar os rótulos do eixo X (overflow: 'justify' -> para habilitar)
                }}
            }},
            plotOptions: {{
                bar: {{
                    dataLabels: {{
                        enabled: true
                    }}
                }}
            }},
            legend: {{
                enabled: false
            }},
            exporting: {{
                enabled: false
            }},
            credits: {{
                enabled: false
            }},
            series: [{{
                name: 'Número de Avaliações por Filme',
                data: data,
                color: '#046D86'
            }}]
        }});
    </script>
</body>
</html>
""".format(data=json.dumps(data), categories=json.dumps(filmes_mais_avaliados4['title'].tolist()), width=chart_width, height=chart_height)
# Gráfico 

# Salvar o HTML em um arquivo
with open('filmes_mais_avaliados3.html', 'w', encoding='utf-8') as f:
    f.write(html_template)    

Nuvem de palavras 1

Além do gráfico de barras feito acima, vamos criar 2 nuvens de palavras para representar os filmes mais assitidos.
A Nuvem de palavras 1 contém os 50 filmes mais avaliados.

Preparar a base 1

# Olhar a tabela criada acima
#filmes_mais_avaliados3
# Selecionar os 50 primeiros valores únicos da coluna 'title'
top_50_titulos1 = filmes_mais_avaliados3['titulo_sem_ano'].unique()[:50]

# Filtrr o DataFrame para selecionar as linhas correspondentes aos 50 primeiros valores únicos de 'title'
top_50_titulos2 = filmes_mais_avaliados3[filmes_mais_avaliados3['titulo_sem_ano'].isin(top_50_titulos1) ] 
top_50_titulos2.head()
# Concatena todos os títulos em uma única string
todos_titulos = ' '.join(top_50_titulos2['titulo_sem_ano'])
print("Quantidade de palavras:", len(todos_titulos))

Nuvem 1

# Lista de Stopword
stop_words = set(STOPWORDS)
# Gerar a WordCloud
wordcloud_titulo1 = WordCloud(stopwords = stop_words,
                             background_color = 'white' , 
                             width = 1600 , height = 800).generate(todos_titulos)

# Plota a WordCloud
plt.figure(figsize=(10, 6))
plt.imshow(wordcloud_titulo1, interpolation='bilinear')
plt.axis('off')
plt.show()                             
# Salvar wordcloud 
#wordcloud_titulo.to_file("wordcloud_titulo1.png")

Nuvem de palavras 2

A Nuvem de palavras 2 contém os 300 filmes mais bem avaliados.Vamos utilizar uma máscara para que a nuvem fique com o formato de uma claquete de cinema.

Primeiro, vamos preparar a base selecionando os filmes e concatenando-os em uma úniva string.

# Selecionar os 300 primeiros valores únicos da coluna 'title'
top_300_titulos1 = filmes_mais_avaliados3['titulo_sem_ano'].unique()[:300]

# Filtra o DataFrame para selecionar as linhas correspondentes aos 300 primeiros valores únicos de 'title'
top_300_titulos2 = filmes_mais_avaliados3[filmes_mais_avaliados3['titulo_sem_ano'].isin(top_300_titulos1) ] 
top_300_titulos2.head()
# Concatena todos os títulos em uma única string
todos_titulos1 = ' '.join(top_300_titulos2['titulo_sem_ano'])
print(f"Quantidade de palavras: {len(todos_titulos1)} , quantidade de palavras únicas:{len(set(todos_titulos1))}")
mascara_1 = np.array(Image.open("C:/0.Projetos/5.Sistema_de_Recomendação_MovieLens_2/Imagens/Análise_Descritiva/Mascaras/mascara_1.png" ))

# Lista de Stopword
stop_words = set(STOPWORDS)
    
# Gerar a word_cloud
wordcloud_titulo4b = WordCloud(stopwords = stop_words,
                             background_color = 'white', 
                             mask = mascara_1,
                             width = 1000 , height = 1000,
                             contour_color='black',
                             contour_width=1,
                             min_font_size= 6,
                             color_func=lambda *args, **kwargs: "black").generate(todos_titulos1)

# Plotar a WordCloud
plt.figure(figsize=(10, 10))
plt.imshow(wordcloud_titulo4b)
plt.axis('off')
plt.show()
# Salvar wordcloud 
wordcloud_titulo.to_file("wordcloud_titulo1b.png")

6.Quais filmes possuem as maiores notas?

Ao analisar a nota, observamos um resultado semelhante ao tópico anterior.Os filmes mais bem avaliados são: The Shawshank Redemption (1994), Pulp Fiction (1994), Matrix (1999) e The Godfather (1972).
Observa-se que a maioria dos filmes com as maiores notas são da década de 70 ao final da década 90. Ou seja, são filmes mais antigos do que se comparado ao tópico anterior.

E, os gêneros que se destacam são comédia, drama e crime.

Preparar a base

# Selecionar as colunas de interesse
tmp_rating = ratings_treino_transformado[['movieId', 'rating_medio_ponderado']]
tmp_movies = movies_treino_transformado[['movieId', 'title', 'titulo_sem_ano', 'genres_separado']]
# Unir as tabelas
tmp_merge = pd.merge( tmp_movies, tmp_rating, on='movieId', how='inner')
tmp_merge.head(3)
# Remover as linhas duplicadas de "title"
top_rating = tmp_merge.drop_duplicates( subset=['title'])

# Organizar de forma decrescente
top_rating1 = top_rating.sort_values(by='rating_medio_ponderado', ascending=False)

# Selecionar os Top 30
top_rating2 = top_rating1.head(100)

Tabela com os 100 filmes com os maiores rating médio (ponderado)

# Os 30 filmes com as maiores notas
top_rating2
del tmp_merge, tmp_movies, tmp_rating

Gráfico com os Filmes com as maiores rating médio (ponderado)

# Criar o gráfico de barras horizontais com Plotly
fig = px.bar(top_rating2, 
             y='title', 
             x='rating_medio_ponderado', 
             orientation='h',
             color_discrete_sequence=['#046D86'],
             title='Os 100 filmes com as maiores notas',
             labels={'Numero_de_Avaliacoes_por_Filme': 'Número de Avaliações por Filme', 
                     'title': 'Título do Filme'})

# Ajustar layout para um visual mais minimalista
fig.update_layout(
    width=1000,
    height=1700,
    title=dict(
        text='Os 100 filmes com as maiores notas',
        x=0.5,  # Centralizar o título
        xanchor='center',
        font=dict(size=20, family='Arial', color='black', weight='bold')  # Negrito
    ),
    xaxis_title_font=dict(size=12, family='Arial', color='black'),
    yaxis_title_font=dict(size=14, family='Arial', color='black'),  # Aumentar tamanho da fonte
    font=dict(size=12),  # Aumentar tamanho da fonte geral
    plot_bgcolor='white',
    yaxis=dict(
        autorange='reversed',  # Inverter a ordem das barras
        tickfont=dict(size=14)  # Aumentar o tamanho dos valores do eixo y
    ),
    margin=dict(t=70, b=20, l=50, r=20)  # Ajustar as margens para mover o gráfico para cima
)

# Mostrar o gráfico
fig.show()
pio.write_html(fig, file='Os_100_filmes_com_as_maiores_notas.html', auto_open=True)

Nuvem de Palavras - Rating Medio Ponderado

Nuvem 1 - Rating Médio Ponderado

# Selecionar os 50 primeiros valores únicos da coluna 'title'
top_titulos_rating = top_rating1['titulo_sem_ano'].unique()[:50]

# Filtra o DataFrame para selecionar as linhas correspondentes aos 50 primeiros valores únicos de 'title'
top_titulos_rating1 = top_rating1[top_rating1['titulo_sem_ano'].isin(top_titulos_rating) ] 

# Concatena todos os títulos em uma única string
todos_titulos_rating = ' '.join(top_titulos_rating1['titulo_sem_ano'])

print(f"Quantidade de palavras: {len(todos_titulos_rating)} , quantidade de palavras únicas:{len(set(todos_titulos_rating))}")
# Lista de Stopword
stop_words = set(STOPWORDS)

# Gerar a WordCloud
wordcloud_titulo_rating2 = WordCloud(stopwords = stop_words,
                             background_color = 'white' , 
                             width = 1600 , height = 800).generate(todos_titulos_rating)

# Plota a WordCloud
plt.figure(figsize=(10, 6))
plt.imshow(wordcloud_titulo_rating2, interpolation='bilinear')
plt.axis('off')
plt.show()    

Nuvem 2 - rating médio ponderado

# Selecionar os 300 primeiros valores únicos da coluna 'title'
top_titulos_rating = top_rating1['titulo_sem_ano'].unique()[:300]

# Filtra o DataFrame para selecionar as linhas correspondentes aos 300 primeiros valores únicos de 'title'
top_titulos_rating1 = top_rating1[top_rating1['titulo_sem_ano'].isin(top_titulos_rating) ] 

# Concatena todos os títulos em uma única string
todos_titulos_rating = ' '.join(top_titulos_rating1['titulo_sem_ano'])

print(f"Quantidade de palavras: {len(todos_titulos_rating)} , quantidade de palavras únicas:{len(set(todos_titulos_rating))}")
mascara_7 = np.array(Image.open("C:/0.Projetos/5.Sistema_de_Recomendação_MovieLens_2/Imagens/Análise_Descritiva/Mascaras/mascara_7.png" ))

# Lista de Stopword
stop_words = set(STOPWORDS)
    
# Gerar a word_cloud
wordcloud_titulo_rating2 = WordCloud(stopwords = stop_words,
                             background_color = 'white', 
                             mask = mascara_1,
                             contour_color='black',
                             contour_width=1,
                             width = 1000 ,
                             height = 1000).generate(todos_titulos_rating)

imagem_colorida_rating = ImageColorGenerator(mascara_7)
wordcloud_titulo_rating2.recolor(color_func=imagem_colorida_rating)                             

# Plotar a WordCloud
plt.figure(figsize=(10, 10))
plt.imshow(wordcloud_titulo_rating2)
plt.axis('off')
plt.show()

7. Quais gêneros possuem mais avaliações? Quais gêneros possuem as maiores notas?

Os gêneros que possuem os maiores rating médio ponderado são respectivamente: Crime, Aventura e Mistério, Sci-Fi e Horror.
Já os gêneros que possuem os maiores números de avaliações são respectivamente: Crime, Aventura, Mistério, Ação e Sci-Fi. Observa-se que os dois resultados são muito semelhantes

Estes resultados também foram encontrados por Lu e Waterman (2005). Os pesquisadores descobriram que os gêneros cinematográficos "propensos à violência" (como "ação", "aventura", "ficção científica" e "terror") aumentaram consideravelmente sua prevalência entre os 20 filmes de maior bilheteria dos EUA no período de 1967 a 2004.

Uma possível explicação encontrada pelos pesquisadores é que estes gêneros aumentaram o uso de tecnologia nos filmes.

Vale ressaltar que o estudo foca nos filmes de maior bilheteria, cujo público alvo são os jovens, conforme será explicado nos próximos tópicos.

Apesar do resultado não ser abrangente, ele ajuda a explicar o porquê destes gêneros terem as maiores notas e mais avaliações.

Preparar a tabela

# Selecionar as colunas de interesse
tmp_movie = movies_treino_transformado[['movieId', 'genres_separado' ]]
tmp_ratings = ratings_treino_transformado[['movieId','Numero_de_Avaliacoes_por_Filme', 'rating_medio_ponderado']]

# Unir as tabelas 
generos_ratings1 = pd.merge(tmp_movie , tmp_ratings, on='movieId' , how='right' )
generos_ratings1.head(3)
generos_ratings2 = generos_ratings1.drop_duplicates(subset=['genres_separado']).sort_values(by=['rating_medio_ponderado'], ascending= False)
#del tmp_movie,tmp_ratings,generos_ratings1

Tabela com gêneros dos filmes, nº de avaliações e rating médio ponderado

generos_ratings2

Gráficos gêneros dos filmes, nº de avaliações e rating médio ponderado

import plotly.graph_objs as go
import plotly.subplots as sp

# Criar uma figura e uma grade de subplots com 1 linha e 2 colunas
fig = sp.make_subplots(
    rows=1, cols=2, 
    subplot_titles=(
        'Gêneros com maior rating médio ponderado',
        'Gêneros com maior número de avaliações'
    ),
    horizontal_spacing=0.2,  # Aumentar o espaçamento horizontal entre os subplots
    vertical_spacing=0.02  # Espaçamento vertical entre os subplots
)

# Primeiro gráfico: rating médio ponderado
fig.add_trace(go.Bar(
    x=generos_ratings2['rating_medio_ponderado'],
    y=generos_ratings2['genres_separado'],
    orientation='h',
    text=[f'{x:.2f}' for x in generos_ratings2['rating_medio_ponderado']],
    textposition='outside',
    marker=dict(color='#03647A')
), row=1, col=1)

# Segundo gráfico: número de avaliações por filme
fig.add_trace(go.Bar(
    x=generos_ratings2['Numero_de_Avaliacoes_por_Filme'],
    y=generos_ratings2['genres_separado'],
    orientation='h',
    text=[f'{x:.0f}' for x in generos_ratings2['Numero_de_Avaliacoes_por_Filme']],
    textposition='outside',
    marker=dict(color='#03647A')
), row=1, col=2)

# Atualizar layout dos gráficos
fig.update_layout(
    height=450,  # Ajustar a altura da figura
    width=900,
    showlegend=False,
    plot_bgcolor='white',
    margin=dict(t=50, b=50, l=50, r=50),  # Ajustar as margens
)

# Atualizar layout do primeiro subplot
fig.update_xaxes(title_text='Rating Médio (Ponderado)', row=1, col=1)
fig.update_yaxes(title_text='Gêneros de Filme', row=1, col=1, tickfont=dict(size=12, family='Arial, bold', weight='bold'))
fig.update_yaxes(autorange="reversed", row=1, col=1)  # Inverter a ordem dos gêneros

# Atualizar layout do segundo subplot
fig.update_xaxes(title_text='Número de Avaliações por Filme', row=1, col=2)
fig.update_yaxes(title_text='Gêneros de Filme', row=1, col=2, tickfont=dict(size=12, family='Arial, bold',weight='bold'))
fig.update_yaxes(autorange="reversed", row=1, col=2)  # Inverter a ordem dos gêneros

# Exibir o gráfico
fig.show()
# Salvar o gráfico como um arquivo HTML
import plotly.io as pio
pio.write_html(fig, file='Ranking_Generos_de_Filme.html', auto_open=True)

8. De quais anos são os filmes mais assistidos? E os mais bem avaliados?

Os filmes mais assistidos são dos anos de 1995, 1994, 1999, 1996 e 2000, respectivamente. Até 1992, a quantidade de avalições por ano cresce timidamente. Entre 1993 e 2000, ela cresce de forma significativa. E, a partir dos anos 2001, a quantidade de avaliações começa a diminuir.

Os filmes mais bem avaliados são os de 1977, 1972, 1975, 1994 e 1980, respectivamente. Ao olhar o gráfico, vemos que as avalições médias por ano aumentam por volta de 1920 e continuam crescendo, com algumas oscilações até 2001.

Os filmes mais bem avaliados são mais antigos, possivelmente, são os filmes considerados clássicos. Já os filmes mais assistidos são um pouco mais novos, entre os anos 90 e início dos anos 2000.Esta foi a época que a internet se popularizou, o que facilitou a divulgação midiática dos filmes.

Em ambos os gráficos, o número de avalições por ano e a contagem de filmes assistidos diminui a partir dos anos 2000.
De acordo com o site Britannica (history of film),no final do século XX houve um aumento da capacidade de animação por computador e de câmeras de vídeo digitais, isso fez com que muitos cineastas utilizassem tais tecnologias para reduzir os custos de produção.

Além disso, nesta mesma época surgiu os DVDs, houve uma ampliação do acesso a TV a cabo e aos cinemas. Esses fatores contribuíram na internacionalização mercado cinematográfico. E com isso, os EUA ampliou seu domínio no cinema mundial. No final da década de 90, a produção de filmes nos EUA focou nos espectadores adolescentes, e de acordo com os críticos, isso diminuiu a qualidade dos filmes.


Portanto, existem dois fatores que podem explicar a queda nos gráficos a partir dos anos 2000: Aumento do uso de tecnologia gráfica nos filmes e foco no público jovem.

Preparando as tabelas

Primeiro, vamos olhar as tabela que serão unidas

movies_treino_transformado.head(3)
ratings_treino_transformado.head(3)

Vamos unir as tabelas

# Unir as tabela e selecionar as colunas de interesse
ano_filme_rating = pd.merge(movies_treino_transformado[['movieId','title','Ano_do_filme']], 
                            ratings_treino_transformado[['movieId', 'rating_medio_ponderado']], 
                            on="movieId", how="inner")
ano_filme_rating.head()                             

Agora, vamos remover as linhas cujo "Ano_do_filme" é igual a -1, pois são as observações em que não foi possível extrair o ano do filme.

# Remover linhas onde a coluna 'Ano_do_filme' tem valor -1
ano_filme_rating1 = ano_filme_rating.loc[ano_filme_rating['Ano_do_filme'] != -1]  

Faremos a contagem de avaliações por ano do filme.

# Fazer a contagem
contagem_movies = ano_filme_rating1[['Ano_do_filme']].value_counts(ascending=False).reset_index()
contagem_movies.head()

Será criada a coluna ratin_medio_por_ano

# Agrupar os dados pela coluna 'Ano_do_filme' e calcular a média da coluna 'rating_medio_ponderado'
rating_medio_por_ano1 = ano_filme_rating1.groupby('Ano_do_filme')['rating_medio_ponderado'].mean().reset_index()

# Renomear a coluna de média para 'rating_medio_por_ano'
rating_medio_por_ano1 = rating_medio_por_ano1.rename(columns={'rating_medio_ponderado': 'rating_medio_por_ano'})
rating_medio_por_ano1.head()

A seguir temos a tabela final

# Unir as tabelas 
movies_ratings = pd.merge(rating_medio_por_ano1, contagem_movies, on="Ano_do_filme", how='inner' )
movies_ratings 

Tabela com a contagem dos filmes mais assistidos

Vamos criar a tabela com os filmes mais assistidos.

movies_ratings_count = movies_ratings.sort_values(by='count',ascending=False)
movies_ratings_count1 = movies_ratings_count.head(5)
movies_ratings_count1.head()

Tabela dos filmes com as maiores avaliações

movies_ratings_top = movies_ratings.sort_values(by='rating_medio_por_ano',ascending=False)
movies_ratings_top1 = movies_ratings_top.head(5)
movies_ratings_top1.head()

Gráfico

# Ordenando por ano
movies_ratings = movies_ratings.sort_values('Ano_do_filme')

# Dados para o gráfico de rating médio por ano
line_rating = go.Scatter(
    x=movies_ratings['Ano_do_filme'],
    y=movies_ratings['rating_medio_por_ano'],
    mode='lines+markers',
    name='Rating Médio',
    line=dict(color='#006868', width=2),
    marker=dict(size=8, color='#006868'),
)

# Destacar os 5 maiores anos de rating médio
top_5_years_rating = movies_ratings.nlargest(5, 'rating_medio_por_ano')
highlighted_points_rating = go.Scatter(
    x=top_5_years_rating['Ano_do_filme'],
    y=top_5_years_rating['rating_medio_por_ano'],
    mode='markers',
    name='Top 5 Anos',
    marker=dict(size=12, color='gold', symbol='star', line=dict(color='black', width=2)),
    showlegend=False,
)

# Dados para o gráfico de contagem por ano
line_count = go.Scatter(
    x=movies_ratings['Ano_do_filme'],
    y=movies_ratings['count'],
    mode='lines+markers',
    name='Contagem',
    line=dict(color='#7D01D6', width=2),
    marker=dict(size=8, color='#7D01D6')
)

# Destacar os 5 maiores anos de contagem
top_5_years_count = movies_ratings.nlargest(5, 'count')
highlighted_points_count = go.Scatter(
    x=top_5_years_count['Ano_do_filme'],
    y=top_5_years_count['count'],
    mode='markers',
    name='Top 5 Anos',
    marker=dict(size=12, color='gold', symbol='star', line=dict(color='black', width=2)),
    showlegend=False,
)

# Criar figuras e subplots
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1,
                    subplot_titles=('Ano dos Filmes com as maiores avaliações', 'Ano dos Filmes mais assitidos'))
fig.add_trace(line_rating, row=1, col=1)
fig.add_trace(highlighted_points_rating, row=1, col=1)
fig.add_trace(line_count, row=2, col=1)
fig.add_trace(highlighted_points_count, row=2, col=1)

# Atualizar layout do subplot de Rating Médio por Ano
fig.update_xaxes(#title_text='Ano do Filme', 
                 tickangle=360, 
                 tickmode='linear', tick0=movies_ratings['Ano_do_filme'].min(), 
                 dtick=5, range=[movies_ratings['Ano_do_filme'].min(), movies_ratings['Ano_do_filme'].max()], row=1, col=1,
                 showgrid=True,gridcolor='rgba(0,0,0,0.1)',)
fig.update_yaxes(title_text='Rating Médio por ano', row=1, col=1,showgrid=True,gridcolor='rgba(0,0,0,0.1)',)


# Atualizar layout do subplot de Contagem por Ano
fig.update_xaxes(title_text='Ano do Filme', tickangle=360, tickmode='linear',
                 tick0=movies_ratings['Ano_do_filme'].min(), dtick=5, range=[movies_ratings['Ano_do_filme'].min(), 
                 movies_ratings['Ano_do_filme'].max()], row=2, col=1,tickfont=dict(size=10),      
                 showgrid=True,gridcolor='rgba(0,0,0,0.1)',)
fig.update_yaxes(title_text='Contagem', row=2, col=1,showgrid=True,gridcolor='rgba(0,0,0,0.1)',)

# Atualizar layout da figura geral
fig.update_layout(
    #title='Ano dos Filmes com as maiores avaliações e mais assistidos',
    title_font=dict(family='Arial', size=24, color='black', weight='bold'),
    width=1100,  # Definindo a largura da figura para 1500 pixels
    plot_bgcolor='white',
    #hovermode='x',
    margin=dict(l=50, r=50, t=50, b=50),
)

# Exibir o gráfico
fig.show()
# Salvar o gráfico como um arquivo HTML
pio.write_html(fig, file='Ano_dos_Filmes_com_as_maiores_avaliacoes_e_mais_assistidos.html', auto_open=True)

Insights

1. Os usuários podem estar insatisfeitos com as recomendações do site

A nota média dada pelo usuário (rating) foi de 3,52 (tópico 2) e essa média diminui conforme o usuário faz mais avaliações (tópicos 3 e 4). Além disso, o número de avaliações por usuário é baixo , a maioria faz 15 avaliações (tópico 3). Estes resultados, podem ser reflexo de uma insatisfação com as recomendações.

Seria interessante a MovieLens investigar o comportamento destes usuários para melhorar as recomendações e aumentar a retenção Para isso, o site poderia:

  • Analisar o número de avaliações vs média (rating) ao longo do tempo: Isto seria interessante para entender se existe um padrão temporal no uso do site MovieLens. Exemplo: por quanto tempo um usuário utiliza ativamente o site?
  • Fazer segmentações: o MovieLens poderia segmentar os usuários por idade e gênero para descobrir como as notas dadas variam em cada grupo e assim, pensar em recomendações personalizadas para cada segmento de usuário.

Espera-se que após estas análises, o MovieLens melhore a média de ratings e aumente o número de avaliações por usuário.

2. Gêneros de destaque

Os gêneros de filme Crime, Aventura e Mistério possuem as maiores notas e os maiores números de avaliações (tópico 7). Para empresas como a Netflix, esta é uma informação relevante para decidir em quais gêneros ela deve investir na hora de produzir um filme.

3. Explorar diferentes de nichos

1. Nicho: Filmes dos anos 90 – 2000

Os filmes mais avaliados são do final da década de 90 e início dos anos 2000, principalmente dos gêneros Ação, Comédia e Crime (tópico 5)

2. Nicho: Filmes clássicos

Os filmes com as maiores avaliações são das décadas de 70 a 90, nos gêneros comédia, drama e crime (tópico 6). Em toda a análise, o gênero drama se destacou apenas neste nicho. Assim, se empresas como a Netflix buscam adquirir direitos autorais de dramas, devem considerar filmes clássicos.

3. Sagas

Apesar das sagas não aparecem no ranking dos filmes mais avaliados e com as maiores notas, as nuvens de palavras dos tópicos 4 e 5 mostram que as sagas ganham destaque quando consideradas em conjunto. Portanto, filmes de sagas podem ser um nicho interessante para aquisição de direitos autorais por plataformas de streaming.

4. Nicho Jovem

Conforme explicado no tópico 7, filmes propensos a violência (Crime, Aventura e Mistério) e que usam mais tecnologia, chamam atenção do público jovem.

5. Nicho pessoas mais velhas

O tópico 8 revela que a partir dos anos 2000 a média de avaliações por ano caiu, possivelmente devido à mudança de foco para o público jovem e ao uso excessivo de computação gráfica nos filmes. Em contraste com o nicho 4, este poderia se concentrar em filmes com menos efeitos visuais e que atendam a um público mais maduro, como filmes independentes.

OBS: O dataset não tem informações sobre os usuários, isso limitou um pouco uma análise mais profunda. Se estas informações estivessem disponíveis, seria interessante analisar a relação de cada nicho de filme com os diferentes perfis de clientes/usuários.